#include "menu.h"

#include "text.h"

#include <SDL2/SDL_image.h>

#include <vector>

struct MenuItem {
    int type = 0;
    SDL_Texture *image = nullptr;
    Text *text = nullptr;
    int group = -1;
    bool checked = false;
    int imgW = 0, imgH = 0, w = 0, h = 0;
};

struct MenuContext {
    SDL_Renderer *renderer;
    TTF_Font *font;
    SDL_Color color;
    int height;
    int align;
    std::vector<MenuItem> items;
    int index = -1;
    std::vector<int> groups;
};

Menu::Menu(SDL_Renderer *renderer, TTF_Font *font, int height, int align, SDL_Color color): context(new MenuContext) {
    context->renderer = renderer;
    context->font = font;
    context->height = height;
    context->color = color;
    context->align = align;
}

Menu::~Menu() {
    for (auto &p: context->items) {
        if (p.image) SDL_DestroyTexture(p.image);
        delete p.text;
    }
    delete context;
}

int Menu::addButton(const char *text, const char *image) {
    MenuItem mi;
    int index = static_cast<int>(context->items.size());
    mi.type = Button;
    if (text) {
        mi.text = new Text(context->renderer, text, context->font, context->color, context->align & AlignVMask);
        mi.text->getSize(&mi.w, &mi.h);
    }
    if (image) {
        mi.image = IMG_LoadTexture(context->renderer, image);
        SDL_QueryTexture(mi.image, nullptr, nullptr, &mi.imgW, &mi.imgH);
        mi.w += mi.imgW;
        mi.h += mi.imgH;
    }
    context->items.emplace_back(mi);
    return index;
}

int Menu::addCheckbox(const char *text, const char *image, bool checked) {
    int res = addButton(text, image);
    if (res < 0) return res;
    auto &mi = context->items[res];
    mi.type = Checkbox;
    mi.checked = checked;
    return res;
}

int Menu::addRadio(const char *text, const char *image, int group, bool checked) {
    int res = addButton(text, image);
    if (res < 0) return res;
    auto &mi = context->items[res];
    mi.type = Radio;
    mi.group = group;
    setChecked(res, checked);
    return res;
}

int Menu::getIndex() {
    return context->index;
}

int Menu::getCount() {
    return static_cast<int>(context->items.size());
}

bool Menu::isChecked(int index) {
    if (index >= static_cast<int>(context->items.size())) return false;
    auto &mi = context->items[context->index];
    return mi.type > 0 && mi.checked;
}

void Menu::setChecked(int index, bool checked) {
    if (index >= static_cast<int>(context->items.size())) return;
    auto &mi = context->items[context->index];
    if (mi.checked == checked) return;
    if (mi.group >= 0) {
        if (mi.group < static_cast<int>(context->groups.size())) {
            if (checked) {
                context->items[context->groups[mi.group]].checked = false;
                context->groups[mi.group] = index;
            } else {
                context->groups[mi.group] = -1;
            }
        } else {
            if (checked) {
                context->groups.resize(mi.group + 1, -1);
                context->groups[mi.group] = index;
            }
        }
    }
    mi.checked = true;
}

int Menu::getRadioChecked(int group) {
    if (group >= static_cast<int>(context->groups.size())) return -1;
    return context->groups[group];
}

int Menu::getType(int index) {
    if (index >= static_cast<int>(context->items.size())) return -1;
    return context->items[index].type;
}

int Menu::processEvents(SDL_Event &ev) {
    return 0;
}

void Menu::render(const SDL_Point &pt) {
    int v_align = context->align & AlignVMask;
    int h_align = context->align & AlignHMask;
    int y = pt.y;
    for (auto &p: context->items) {
        int x = pt.x;
        switch (h_align) {
            case AlignHCenter: x -= p.w / 2; break;
            case AlignHRight: x -= p.w; break;
            default: break;
        }
        if (p.image) {
            int ydelta;
            switch(v_align) {
                case AlignVCenter: ydelta = (context->height - p.imgH) / 2; break;
                case AlignVBottom: ydelta = context->height - p.imgH; break;
                default: ydelta = 0; break;
            }
            SDL_Rect rc{x, y + ydelta, p.imgW, p.imgH};
            SDL_RenderCopy(context->renderer, p.image, nullptr, &rc);
        }
        if (p.text) p.text->render(SDL_Point{x + p.imgW, y});
        y += context->height;
    }
}
